package com.yaxin.oss.core;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import com.yaxin.common.utils.DateUtils;
import com.yaxin.common.utils.StringUtils;
import com.yaxin.oss.constant.OssConstant;
import com.yaxin.oss.entity.UploadResult;
import com.yaxin.oss.enumd.AccessPolicyType;
import com.yaxin.oss.enumd.PolicyType;
import com.yaxin.oss.exception.OssException;
import com.yaxin.oss.properties.OssProperties;
import io.minio.*;
import io.minio.http.Method;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

public class OssMinioClient {

    private final String configKey;
    private final OssProperties properties;
    private final MinioClient client;

    public OssMinioClient(String configKey, OssProperties ossProperties) {
        this.configKey = configKey;
        this.properties = ossProperties;
        try {
            // 关键修正：只使用主机和端口，不包含路径
            String endpoint = OssConstant.IS_HTTPS.equals(properties.getIsHttps())
                ? "https://" + properties.getEndpoint()
                : "http://" + properties.getEndpoint();

            System.out.println("Connecting to MinIO at: " + endpoint);
            this.client = MinioClient.builder()
                .endpoint(endpoint)
                .credentials(properties.getAccessKey(), properties.getSecretKey())
                .build();

            createBucket();
        } catch (Exception e) {
            throw new OssException("配置错误! 请检查系统配置:[" + e.getMessage() + "]");
        }
    }


    public void createBucket() {
        try {
            String bucketName = properties.getBucketName();
            boolean exists = client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!exists) {
                client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                client.setBucketPolicy(SetBucketPolicyArgs.builder()
                    .bucket(bucketName)
                    .config(getPolicy(bucketName, getAccessPolicy().getPolicyType()))
                    .build());
            }
        } catch (Exception e) {
            throw new OssException("创建Bucket失败，请核对配置信息:[" + e.getMessage() + "]");
        }
    }

    public UploadResult upload(InputStream inputStream, String path, String contentType) {
        try {
            if (!(inputStream instanceof ByteArrayInputStream)) {
                inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream));
            }
            client.putObject(PutObjectArgs.builder()
                .bucket(properties.getBucketName())
                .object(path)
                .stream(inputStream, inputStream.available(), -1)
                .contentType(contentType)
                .build());
        } catch (Exception e) {
            throw new OssException("上传文件失败，请检查配置信息:[" + e.getMessage() + "]");
        }
        return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build();
    }

    public UploadResult upload(byte[] data, String suffix, String contentType) {
        return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix), contentType);
    }

    public void delete(String path) {
        path = path.replace(getUrl() + "/", "");
        try {
            client.removeObject(RemoveObjectArgs.builder()
                .bucket(properties.getBucketName())
                .object(path)
                .build());
        } catch (Exception e) {
            throw new OssException("删除文件失败，请检查配置信息:[" + e.getMessage() + "]");
        }
    }

    public InputStream getObjectContent(String path) {
        path = path.replace(getUrl() + "/", "");
        try {
            return client.getObject(GetObjectArgs.builder()
                .bucket(properties.getBucketName())
                .object(path)
                .build());
        } catch (Exception e) {
            throw new OssException("获取文件失败，请检查配置信息:[" + e.getMessage() + "]");
        }
    }

    public String getPrivateUrl(String objectKey, Integer second) {
        try {
            return client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(properties.getBucketName())
                .object(objectKey)
                .expiry(second, TimeUnit.SECONDS)
                .build());
        } catch (Exception e) {
            throw new OssException("生成签名URL失败:[" + e.getMessage() + "]");
        }
    }

    public String getUrl() {
        String domain = properties.getDomain();
        String endpoint = properties.getEndpoint();
        String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://";
        if (StringUtils.isNotBlank(domain)) {
            return header + domain + "/" + properties.getBucketName();
        }
        return header + endpoint + "/" + properties.getBucketName();
    }

    public String getPath(String prefix, String suffix) {
        String uuid = IdUtil.fastSimpleUUID();
        String path = DateUtils.datePath() + "/" + uuid;
        if (StringUtils.isNotBlank(prefix)) {
            path = prefix + "/" + path;
        }
        return path + suffix;
    }

    /**
     * 检查配置是否相同
     */
    public boolean checkPropertiesSame(OssProperties properties) {
        return this.properties.equals(properties);
    }

    public AccessPolicyType getAccessPolicy() {
        return AccessPolicyType.getByType(properties.getAccessPolicy());
    }

    public String getConfigKey() {
        return configKey;
    }

    /**
     * 根据权限类型生成 MinIO 桶策略 JSON（符合 S3 语法）
     *
     * @param bucketName 桶名称
     * @param policyType 权限类型（READ, WRITE, READ_WRITE）
     * @return JSON 格式的桶策略
     */
    private static String getPolicy(String bucketName, PolicyType policyType) {
        String bucketResource = "arn:aws:s3:::" + bucketName;
        String bucketObjectsResource = bucketResource + "/*";

        StringBuilder policy = new StringBuilder();
        policy.append("{\n");
        policy.append("  \"Version\": \"2012-10-17\",\n");
        policy.append("  \"Statement\": [\n");

        // Bucket-level permissions
        policy.append("    {\n");
        policy.append("      \"Effect\": \"Allow\",\n");
        policy.append("      \"Principal\": { \"AWS\": [\"*\"] },\n");
        policy.append("      \"Action\": [\n");

        if (policyType == PolicyType.WRITE) {
            policy.append("        \"s3:GetBucketLocation\",\n");
            policy.append("        \"s3:ListBucketMultipartUploads\"\n");
        } else if (policyType == PolicyType.READ_WRITE) {
            policy.append("        \"s3:GetBucketLocation\",\n");
            policy.append("        \"s3:ListBucket\",\n");
            policy.append("        \"s3:ListBucketMultipartUploads\"\n");
        } else { // READ
            policy.append("        \"s3:GetBucketLocation\"\n");
        }

        policy.append("      ],\n");
        policy.append("      \"Resource\": \"" + bucketResource + "\"\n");
        policy.append("    },\n");

        // Object-level permissions
        policy.append("    {\n");
        policy.append("      \"Effect\": \"Allow\",\n");
        policy.append("      \"Principal\": { \"AWS\": [\"*\"] },\n");
        policy.append("      \"Action\": [\n");

        switch (policyType) {
            case WRITE:
                policy.append("        \"s3:AbortMultipartUpload\",\n");
                policy.append("        \"s3:DeleteObject\",\n");
                policy.append("        \"s3:ListMultipartUploadParts\",\n");
                policy.append("        \"s3:PutObject\"\n");
                break;
            case READ_WRITE:
                policy.append("        \"s3:AbortMultipartUpload\",\n");
                policy.append("        \"s3:DeleteObject\",\n");
                policy.append("        \"s3:GetObject\",\n");
                policy.append("        \"s3:ListMultipartUploadParts\",\n");
                policy.append("        \"s3:PutObject\"\n");
                break;
            default: // READ
                policy.append("        \"s3:GetObject\"\n");
                break;
        }

        policy.append("      ],\n");
        policy.append("      \"Resource\": \"" + bucketObjectsResource + "\"\n");
        policy.append("    }\n");

        policy.append("  ]\n");
        policy.append("}");

        return policy.toString();
    }

}
